/*
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*         http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

#include <gtest/gtest.h>
#include <test_utilities/test_base.h>
#include "cx5_card.h"
#include "cx5_port.h"
#include <mc/engine.h>
#include <mc/common.h>
#include <mc/variant.h>
#include <ctime>
#include <vector>

namespace drivers {
namespace pcie_nic_card {
namespace mellanox {
namespace cx5 {
namespace test {

// Mock IPMI Core 接口，避免真实的 D-Bus 调用
class test_ipmi_core_interface : public mc::engine::interface<test_ipmi_core_interface> {
public:
    MC_INTERFACE("bmc.kepler.IpmiCore")

    // a{ss}yyyyyay
    mc::variant Request(
        const mc::dict& ctx,              // a{ss} - 上下文字典
        uint8_t channel_type,              // y - 通道类型
        uint8_t instance,                  // y - 实例序号
        uint8_t net_fn,                   // y - 网络功能码
        uint8_t lun,                      // y - 逻辑单元号
        uint8_t cmd,                      // y - 命令字
        const std::vector<uint8_t>& data  // ay - 命令参数数据
    ) {
        ilog("test_ipmi_core_interface: Request - net_fn=${net_fn}, cmd=${cmd}", ("net_fn", net_fn)("cmd", cmd));
        // 返回模拟的成功响应，避免真实的 D-Bus 调用
        mc::variants result = {mc::variant(static_cast<int64_t>(0)), mc::variant(std::vector<uint8_t>{0x00, 0x01, 0x02})};
        return mc::variant(result);
    }
};

class test_ipmi_core_object : public mc::engine::object<test_ipmi_core_object> {
public:
    MC_OBJECT(test_ipmi_core_object, "bmc.kepler.IpmiCore", "/bmc/kepler/IpmiCore", (test_ipmi_core_interface))

    test_ipmi_core_interface m_test_interface;
};

class cx5_test_service : public mc::engine::service {
public:
    cx5_test_service(const std::string& name) : mc::engine::service(name) {}
};

// 辅助函数：清理 cx5_card 的成员变量，避免析构时访问未初始化的指针
static void safe_cleanup_cx5_card(dev::cx5_card* card) {
    if (card == nullptr) {
        return;
    }
    // 在析构前先清理成员变量的 NCSI 任务
    try {
        card->m_network_adapter.stop_ncsi_update_task();
    } catch (...) {
        // 忽略可能的异常
    }
    try {
        card->m_network_adapter_cooling.stop_ncsi_update_task();
    } catch (...) {
        // 忽略可能的异常
    }
    try {
        card->m_pcie_device.stop_ncsi_update_task();
    } catch (...) {
        // 忽略可能的异常
    }
    try {
        card->m_network_adapter.unregister_os_reset_callback();
    } catch (...) {
        // 忽略可能的异常
    }
}

// 自定义删除器：在删除前先清理资源
struct cx5_card_deleter {
    void operator()(dev::cx5_card* card) const {
        safe_cleanup_cx5_card(card);
        delete card;
    }
};

// ========================= cx5_card测试 =========================
class Cx5CardTest : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        // 使用与 IPMB 库期望的服务名一致的服务名，避免超时
        // IPMB 库查找的服务名是 "bmc.kepler.ipmi_core"（定义在 libraries/ipmb/ipmb.h）
        m_test_service = std::make_unique<cx5_test_service>("bmc.kepler.ipmi_core");
        m_test_service->init();
        m_test_service->start();
        
        // 注册 mock IpmiCore 接口，避免真实的 D-Bus 调用
        m_ipmi_core_obj = test_ipmi_core_object::create();
        m_ipmi_core_obj->set_object_path("/bmc/kepler/IpmiCore");
        m_ipmi_core_obj->set_object_name("bmc.kepler.IpmiCore");
        m_test_service->register_object(m_ipmi_core_obj);
    }
    static void TearDownTestSuite() {
        try {
            if (m_ipmi_core_obj && m_test_service) {
                m_test_service->unregister_object(m_ipmi_core_obj);
                m_ipmi_core_obj.reset();
            }
            if (m_test_service) {
                m_test_service->stop();
                m_test_service.reset();
            }
            TestWithEngine::TearDownTestSuite();
        } catch (...) {
            if (m_ipmi_core_obj) {
                m_ipmi_core_obj.reset();
            }
            if (m_test_service) {
                m_test_service.reset();
            }
        }
    }
    void SetUp() override {
        static int test_counter = 0;
        test_counter++;
        std::string object_name = "test_cx5_card_" + std::to_string(test_counter) + "_" + std::to_string(std::time(nullptr));
        std::string position = std::to_string(test_counter + 100);

        m_card = mc::make_shared<dev::cx5_card>();
        m_card->set_service(m_test_service.get());
        m_card->set_object_name(object_name.c_str());
        m_card->set_position(position.c_str());
        try {
            m_test_service->register_object(m_card);
        } catch (...) {
            // 忽略注册时的异常，可能在某些测试中不需要注册
        }
    }
    void TearDown() override {
        try {
            if (m_card && m_test_service) {
                try {
                    m_card->stop();
                } catch (...) {
                    // 忽略 stop 时的异常（对象可能未初始化）
                }
                try {
                    m_test_service->unregister_object(m_card);
                } catch (...) {
                    // 忽略注销时的异常
                }
            }
            m_card.reset();
        } catch (...) {
            if (m_card) {
                m_card.reset();
            }
        }
    }

    mc::mutable_dict create_test_csr() {
        return mc::mutable_dict{
            {"SystemId", 1}
        };
    }

    mc::dict create_test_connector() {
        return mc::dict{{"SystemId", 1}};
    }

    mc::shared_ptr<dev::cx5_port> create_test_port(uint8_t port_id, bool set_parent = false) {
        auto port = mc::make_shared<dev::cx5_port>();
        port->set_service(m_test_service.get());
        std::string port_name = "NicPort_" + std::to_string(port_id);
        port->set_object_name(port_name.c_str());
        port->set_position(std::to_string(port_id).c_str());
        if (set_parent) {
            port->set_parent(m_card.get());
        }
        m_test_service->register_object(port);
        return port;
    }

    static std::unique_ptr<cx5_test_service> m_test_service;
    static mc::shared_ptr<test_ipmi_core_object> m_ipmi_core_obj;
    mc::shared_ptr<dev::cx5_card> m_card;
};
std::unique_ptr<cx5_test_service> Cx5CardTest::m_test_service;
mc::shared_ptr<test_ipmi_core_object> Cx5CardTest::m_ipmi_core_obj;

} // namespace test
} // namespace cx5
} // namespace mellanox
} // namespace pcie_nic_card
} // namespace drivers

// 添加反射元数据定义（必须在命名空间外）
MC_REFLECT(drivers::pcie_nic_card::mellanox::cx5::test::test_ipmi_core_interface, (Request))
MC_REFLECT(drivers::pcie_nic_card::mellanox::cx5::test::test_ipmi_core_object, ((m_test_interface, "bmc.kepler.IpmiCore")))

using namespace drivers::pcie_nic_card::mellanox::cx5::test;

// ========================= 基本功能测试 =========================

TEST_F(Cx5CardTest, Constructor) {
    // Constructor 测试只验证对象能否正确创建
    // 直接创建栈对象，无需复杂配置，由 RAII 自动管理生命周期
    dev::cx5_card card;
    EXPECT_TRUE(true);
}

TEST_F(Cx5CardTest, StartStop_Basic) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);

    EXPECT_TRUE(m_card->start());
    EXPECT_TRUE(m_card->stop());
}

TEST_F(Cx5CardTest, Start_WithPorts) {
    static int port_counter = 0;
    port_counter++;
    std::string port1_name = "NicPort_" + std::to_string(port_counter) + "_" + std::to_string(std::time(nullptr));
    std::string port2_name = "NicPort_" + std::to_string(port_counter + 1) + "_" + std::to_string(std::time(nullptr));
    std::string port1_position = std::to_string(port_counter + 2000);
    std::string port2_position = std::to_string(port_counter + 2001);

    auto port1 = mc::make_shared<dev::cx5_port>();
    port1->set_service(m_test_service.get());
    port1->set_object_name(port1_name.c_str());
    port1->set_position(port1_position.c_str());
    port1->set_parent(m_card.get());

    auto port2 = mc::make_shared<dev::cx5_port>();
    port2->set_service(m_test_service.get());
    port2->set_object_name(port2_name.c_str());
    port2->set_position(port2_position.c_str());
    port2->set_parent(m_card.get());

    auto csr1 = create_test_csr();
    csr1["PortId"] = port_counter;
    auto connector = create_test_connector();
    port1->init(csr1, connector);

    auto csr2 = create_test_csr();
    csr2["PortId"] = port_counter + 1;
    port2->init(csr2, connector);

    m_test_service->register_object(port1);
    m_test_service->register_object(port2);

    m_card->init_network_ports();

    auto card_csr = create_test_csr();
    m_card->init(card_csr, connector);
    bool result = m_card->start();
    EXPECT_TRUE(result);

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    m_card->stop();
    port1->stop();
    port2->stop();
    m_test_service->unregister_object(port1);
    m_test_service->unregister_object(port2);
}

TEST_F(Cx5CardTest, Init_Success) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    bool result = m_card->init(csr, connector);
    EXPECT_TRUE(result);
}

TEST_F(Cx5CardTest, Init_Exception) {
    mc::mutable_dict empty_csr;
    mc::dict empty_connector;
    bool result = m_card->init(empty_csr, empty_connector);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5CardTest, Init_WithInvalidData) {
    mc::mutable_dict invalid_csr;
    invalid_csr["SystemId"] = "invalid_string";
    mc::dict connector = create_test_connector();

    bool result = m_card->init(invalid_csr, connector);
    EXPECT_TRUE(result == true || result == false);
}

// ========================= 多次操作测试 =========================

TEST_F(Cx5CardTest, MultipleStartStop) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);

    EXPECT_TRUE(m_card->start());
    EXPECT_TRUE(m_card->stop());
}

// ========================= 网络端口相关测试 =========================

TEST_F(Cx5CardTest, InitNetworkPorts_Empty) {
    m_card->init_network_ports();
    EXPECT_TRUE(true);
}

TEST_F(Cx5CardTest, InitNetworkPorts_WithPorts) {
    static int port_counter = 0;
    port_counter++;
    std::string port1_name = "NicPort_" + std::to_string(port_counter) + "_" + std::to_string(std::time(nullptr));
    std::string port2_name = "NicPort_" + std::to_string(port_counter + 1) + "_" + std::to_string(std::time(nullptr));
    std::string port1_position = std::to_string(port_counter + 3000);
    std::string port2_position = std::to_string(port_counter + 3001);

    auto port1 = mc::make_shared<dev::cx5_port>();
    port1->set_service(m_test_service.get());
    port1->set_object_name(port1_name.c_str());
    port1->set_position(port1_position.c_str());
    port1->set_parent(m_card.get());

    auto port2 = mc::make_shared<dev::cx5_port>();
    port2->set_service(m_test_service.get());
    port2->set_object_name(port2_name.c_str());
    port2->set_position(port2_position.c_str());
    port2->set_parent(m_card.get());

    auto csr1 = create_test_csr();
    csr1["PortId"] = port_counter;
    auto connector = create_test_connector();
    port1->init(csr1, connector);

    auto csr2 = create_test_csr();
    csr2["PortId"] = port_counter + 1;
    port2->init(csr2, connector);

    m_test_service->register_object(port1);
    m_test_service->register_object(port2);

    m_card->init_network_ports();

    auto card_csr = create_test_csr();
    m_card->init(card_csr, connector);
    m_card->start();

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    m_card->stop();
    port1->stop();
    port2->stop();
    m_test_service->unregister_object(port1);
    m_test_service->unregister_object(port2);
}

TEST_F(Cx5CardTest, InitNetworkPorts_WithPortsAndNonPorts) {
    static int port_counter = 0;
    port_counter++;
    std::string port1_name = "NicPort_" + std::to_string(port_counter) + "_" + std::to_string(std::time(nullptr));
    std::string port2_name = "NicPort_" + std::to_string(port_counter + 1) + "_" + std::to_string(std::time(nullptr));
    std::string port1_position = std::to_string(port_counter + 4000);
    std::string port2_position = std::to_string(port_counter + 4001);

    auto port1 = mc::make_shared<dev::cx5_port>();
    port1->set_service(m_test_service.get());
    port1->set_object_name(port1_name.c_str());
    port1->set_position(port1_position.c_str());
    port1->set_parent(m_card.get());

    auto port2 = mc::make_shared<dev::cx5_port>();
    port2->set_service(m_test_service.get());
    port2->set_object_name(port2_name.c_str());
    port2->set_position(port2_position.c_str());
    port2->set_parent(m_card.get());

    auto csr1 = create_test_csr();
    csr1["PortId"] = port_counter;
    auto connector = create_test_connector();
    port1->init(csr1, connector);

    auto csr2 = create_test_csr();
    csr2["PortId"] = port_counter + 1;
    port2->init(csr2, connector);

    m_test_service->register_object(port1);
    m_test_service->register_object(port2);

    auto non_port = mc::make_shared<dev::cx5_card>();
    non_port->set_service(m_test_service.get());
    non_port->set_object_name("OtherObject_1");
    non_port->set_position("998");
    non_port->set_parent(m_card.get());
    m_test_service->register_object(non_port);

    m_card->init_network_ports();

    EXPECT_TRUE(true);

    non_port->stop();
    port1->stop();
    port2->stop();
    m_test_service->unregister_object(non_port);
    m_test_service->unregister_object(port1);
    m_test_service->unregister_object(port2);
}

// ========================= 异常处理测试 =========================

TEST_F(Cx5CardTest, Start_WithoutInit) {
    bool result = m_card->start();
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5CardTest, Stop_WithoutStart) {
    bool result = m_card->stop();
    EXPECT_TRUE(result);
}

TEST_F(Cx5CardTest, Stop_WithTimer) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);
    m_card->start();

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    bool result = m_card->stop();
    EXPECT_TRUE(result);
}

// ========================= 端口BDF更新测试 =========================

TEST_F(Cx5CardTest, UpdatePortsBdfAsync_EmptyPorts) {
    dev::imu_pcie_device_req_t pcie_device_info = {
        .system_id = 1,
        .socket_id = 0,
        .bus = 0x18,
        .device = 0x02,
        .function = 0x03,
        .pci_info_address = dev::VID_DID_ADDRESS,
        .rlen = 4
    };

    m_card->update_ports_bdf_async(pcie_device_info);
    EXPECT_TRUE(true);
}

TEST_F(Cx5CardTest, UpdatePortsBdfAsync_WithPorts) {
    static int port_counter = 0;
    port_counter++;
    std::string port_name = "NicPort_" + std::to_string(port_counter) + "_" + std::to_string(std::time(nullptr));
    std::string port_position = std::to_string(port_counter + 5000);

    auto port = mc::make_shared<dev::cx5_port>();
    port->set_service(m_test_service.get());
    port->set_object_name(port_name.c_str());
    port->set_position(port_position.c_str());
    port->set_parent(m_card.get());
    m_test_service->register_object(port);

    auto csr = create_test_csr();
    csr["PortId"] = port_counter;
    auto connector = create_test_connector();
    port->init(csr, connector);

    m_card->init_network_ports();

    dev::imu_pcie_device_req_t pcie_device_info = {
        .system_id = 1,
        .socket_id = 0,
        .bus = 0x18,
        .device = 0x02,
        .function = 0x03,
        .pci_info_address = dev::VID_DID_ADDRESS,
        .rlen = 4
    };

    m_card->update_ports_bdf_async(pcie_device_info);

    port->stop();
    m_test_service->unregister_object(port);
}

TEST_F(Cx5CardTest, UpdatePortsBdfAsync_WithMultiplePorts) {
    static int port_counter = 0;
    port_counter++;
    uint8_t port_id1 = port_counter;
    uint8_t port_id2 = port_counter + 1;
    
    // 简化端口创建：使用唯一标识符，避免路径冲突
    // 不使用时间戳，改用静态计数器确保唯一性
    std::string port1_name = "NicPort_" + std::to_string(port_counter) + "_bdf";
    std::string port2_name = "NicPort_" + std::to_string(port_counter + 1) + "_bdf";
    std::string port1_position = std::to_string(port_counter + 6000);
    std::string port2_position = std::to_string(port_counter + 6001);

    auto port1 = mc::make_shared<dev::cx5_port>();
    port1->set_service(m_test_service.get());
    port1->set_object_name(port1_name.c_str());
    port1->set_position(port1_position.c_str());
    port1->set_parent(m_card.get());

    auto port2 = mc::make_shared<dev::cx5_port>();
    port2->set_service(m_test_service.get());
    port2->set_object_name(port2_name.c_str());
    port2->set_position(port2_position.c_str());
    port2->set_parent(m_card.get());

    // 初始化端口：先设置 PortId，再 init，确保 PortId 在路径生成前已设置
    auto connector = create_test_connector();
    auto csr1 = create_test_csr();
    csr1["PortId"] = port_id1;
    port1->init(csr1, connector);

    auto csr2 = create_test_csr();
    csr2["PortId"] = port_id2;
    port2->init(csr2, connector);

    // 在注册对象前，确保 PortId 已正确设置
    // 由于对象路径基于 PortId，必须在 init 之后注册
    m_test_service->register_object(port1);
    m_test_service->register_object(port2);

    // 初始化网络端口列表
    m_card->init_network_ports();

    // 测试 BDF 更新
    dev::imu_pcie_device_req_t pcie_device_info = {
        .system_id = 1,
        .socket_id = 0,
        .bus = 0x18,
        .device = 0x02,
        .function = 0x03,
        .pci_info_address = dev::VID_DID_ADDRESS,
        .rlen = 4
    };

    m_card->update_ports_bdf_async(pcie_device_info);

    // 清理资源
    port1->stop();
    port2->stop();
    m_test_service->unregister_object(port1);
    m_test_service->unregister_object(port2);
}

// ========================= 动态属性替换测试 =========================

TEST_F(Cx5CardTest, ReplaceDynamicProperty) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    csr["bmc.dev.PCIeDevice"] = 
    mc::mutable_dict{{"DeviceName", "test_cx5_card${Slot}"}, 
    {"Slot", "${Slot}"}};
    csr["bmc.dev.Board"] = mc::mutable_dict{{"Slot", "${Slot}"}};
    connector = mc::dict{{"Slot", 1}};
    m_card->replace_dynamic_property(csr, connector);
    EXPECT_TRUE(true);
}

TEST_F(Cx5CardTest, MultipleReplaceDynamicProperty) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    csr["bmc.dev.PCIeDevice"] = 
    mc::mutable_dict{{"DeviceName", "test_cx5_card${Slot}"}, 
    {"Slot", "${Slot}"}};
    csr["bmc.dev.Board"] = mc::mutable_dict{{"Slot", "${Slot}"}};
    connector = mc::dict{{"Slot", 1}};

    m_card->replace_dynamic_property(csr, connector);
    m_card->replace_dynamic_property(csr, connector);
    EXPECT_TRUE(true);
}

// ========================= 多次调用测试 =========================

TEST_F(Cx5CardTest, MultipleInit) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();

    EXPECT_TRUE(m_card->init(csr, connector));
    EXPECT_TRUE(m_card->init(csr, connector));
}

// ========================= 属性设置测试 =========================

TEST_F(Cx5CardTest, SetSystemId) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);

    EXPECT_EQ(m_card->m_system_id, 1);
}

TEST_F(Cx5CardTest, SetPcieDeviceBdf) {
    m_card->m_pcie_device.DevBus = 0x19;
    m_card->m_pcie_device.DevDevice = 0x03;
    m_card->m_pcie_device.DevFunction = 0x04;

    EXPECT_EQ(m_card->m_pcie_device.DevBus.get_value().as<uint8_t>(), 0x19);
    EXPECT_EQ(m_card->m_pcie_device.DevDevice.get_value().as<uint8_t>(), 0x03);
    EXPECT_EQ(m_card->m_pcie_device.DevFunction.get_value().as<uint8_t>(), 0x04);
}

// ========================= 析构函数测试 =========================

TEST_F(Cx5CardTest, Destructor_CleansUpResources) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);
    m_card->start();

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    m_card->stop();
    m_card.reset();

    EXPECT_TRUE(m_card == nullptr);
}

TEST_F(Cx5CardTest, Destructor_WithTimerAndObjects) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);
    m_card->start();

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    m_card.reset();

    EXPECT_TRUE(m_card == nullptr);
}

// ========================= IMU协议初始化测试 =========================

TEST_F(Cx5CardTest, InitImuProtocol_Success) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);

    bool result = m_card->start();
    EXPECT_TRUE(result);

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    m_card->stop();
}

TEST_F(Cx5CardTest, InitImuProtocol_ExceptionHandling) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_card->init(csr, connector);

    bool result = m_card->start();
    EXPECT_TRUE(result);

    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    m_card->stop();
}

// ========================= 多实例测试 =========================

TEST_F(Cx5CardTest, MultiInstance) {
    std::vector<mc::shared_ptr<dev::cx5_card>> cards;
    for (int i = 0; i < 3; i++) {
        std::string object_name = "card_" + std::to_string(i) + "_" + std::to_string(std::time(nullptr)) + "_" + std::to_string(i * 1000);
        std::string position = std::to_string(i + 1000);

        auto card = mc::make_shared<dev::cx5_card>();
        card->set_service(m_test_service.get());
        card->set_object_name(object_name.c_str());
        card->set_position(position.c_str());
        m_test_service->register_object(card);

        auto csr = create_test_csr();
        auto connector = create_test_connector();
        card->init(csr, connector);
        EXPECT_TRUE(card->start());
        cards.push_back(card);
    }
    for (auto& card : cards) {
        if (card && m_test_service) {
            try {
                card->stop();
                m_test_service->unregister_object(card);
            } catch (...) {
            }
        }
    }
    cards.clear();
}
